/*
 * Copyright (C) 2009 Juan Alberto López Cavallotti
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; version 2
 * of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package filesystem;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author juan
 */
public class DirWatcherDaemon extends Thread {


    private File dirWatched;
    private HashMap<String, Long> modsDirs;
    private HashSet<String> filesChecked;
    private ArrayList<FileSystemListener> listeners;

    public DirWatcherDaemon(String dirName) {

        listeners = new ArrayList<FileSystemListener>();
        dirWatched = new File(dirName);

        if (!dirWatched.isDirectory())
            throw new Error("Not a directory");

        //generar recursivamente los tiempos de modificacion
        modsDirs = new HashMap<String, Long>();
        filesChecked = new HashSet<String>();
        generateTimes(dirWatched);

        setDaemon(true);
    }

    public void addListener(FileSystemListener listener) {
        listeners.add(listener);
    }

    @Override
    public void run() {
        
        
        while(true) {

            checkTimes(dirWatched);
            verifyDeleted();

            try {
                Thread.sleep(2000);
            } catch (InterruptedException ex) {
                Logger.getLogger(DirWatcherDaemon.class.getName()).log(Level.SEVERE, null, ex);
                break;
            }
        }
    }

    protected void fireFileUpdated(File file) {
        
        final File archivo = file;

        new Thread() {

            @Override
            public void run() {
                for (FileSystemListener fsl : listeners) {
                    fsl.fileModified(archivo);
                }
            }

        }.start();
    }

    private void fireFileAdded(File file) {

        final File archivo = file;

        new Thread() {

            @Override
            public void run() {
                for (FileSystemListener fsl : listeners) {
                    fsl.fileAdded(archivo);
                }
            }

        }.start();
    }

    private void fireFileDeleted(String s) {

        final String cadena = s;

        new Thread() {

            @Override
            public void run() {
                for (FileSystemListener fsl : listeners) {
                    fsl.fileDeleted(cadena);
                }
            }

        }.start();
    }

    private void generateTimes(File raiz) {
        File[] list = raiz.listFiles();

        for(int i = 0; i < list.length; i++) {

            String key = list[i].getPath();

            //generar un poco de output
            //System.out.println(key);
            long lastModified = list[i].lastModified();

            if (list[i].isDirectory()) {
                //llamada recursiva
                generateTimes(list[i]);
            }
            modsDirs.put(key, lastModified);
        }
    }

    private void checkTimes(File raiz) {

        File[] list = raiz.listFiles();

        for(int i = 0; i < list.length; i++) {
            String key = list[i].getPath();
            long tiempo = list[i].lastModified();

            //cada vez que se revisa un archivo se lo añade a la lista de revisados
            filesChecked.add(key);
            if (modsDirs.containsKey(key)) {
                long bufferTiempo = modsDirs.get(key);
                if (bufferTiempo != tiempo) {
                    modsDirs.put(key, tiempo);
                    fireFileUpdated(list[i]);
                }
            } else {
                modsDirs.put(key, tiempo);
                fireFileAdded(list[i]);
            }

            if (list[i].isDirectory())
            {
                checkTimes(list[i]);
            }
        }
    }

   private void verifyDeleted() {
        //recorrer las llaves de la lista de tiempos
        for(String s : modsDirs.keySet()) {
            if (filesChecked.contains(s)) //significa que no se ha borrado
                filesChecked.remove(s);
            else
                filesChecked.add(s); //
        }

        //al final del bucle anterior tenemos en filesChecked solo los archivos borrados
        for(String s : filesChecked) {
            modsDirs.remove(s);
            fireFileDeleted(s);
        }

        filesChecked.clear();
    }

}
